#pragma once

#include "stdafx.h"

namespace natrium {
	namespace util {
		//predicate structure
		struct IsSpace: public std::unary_function<char, bool> {
			#if OA_PLATFORM == PLATFORM_WIN32
				IsSpace(const std::locale& loc = std::locale("english")): mLocale(loc) {}

			#elif OA_PLATFORM == PLATFORM_APPLE
				IsSpace(const std::locale& loc = std::locale("C")): mLocale(loc) {}
			
			#else
				IsSpace(const std::locale& loc = std::locale("en_US.UTF-8")): mLocale(loc) {}

			#endif

			bool operator()(char c) const { return std::isspace(c, mLocale); }

		private:
			std::locale mLocale;
		};

		//predicate structure
		struct IsSlash: public std::unary_function<char, bool> {
			bool operator () (char c) const { return c == '/'; }
		};

		//predicate structure
		struct IsDelimeter: public std::unary_function<char, bool> {
			IsDelimeter(char delimeter): mDelimeter(delimeter) {}

			bool operator () (char c) const { return c == mDelimeter; }

		private:
			char mDelimeter;
		};

		//the tPredicate should have a bool operator()(char c) that returns true on a seperation character
		template <class tPredicate = IsSpace>
		class StringTokenizer {
		public:
			static void tokenize(
				const std::string& input, 
				std::vector<std::string>& output, 
				const tPredicate& predicate = tPredicate()
			);
		};

		template <class tPredicate>
		inline void StringTokenizer<tPredicate>::tokenize(const std::string& input, std::vector<std::string>& output, const tPredicate& predicate) {
			output.clear();

			std::string::const_iterator it = input.begin();
			std::string::const_iterator tokenEnd = input.begin();

			while (it != input.end()) {
				if (predicate(*it)) //if separator character was found
					++it; //just skip it
				else { 
					tokenEnd = std::find_if(it, input.end(), predicate); //find the end of the current token
					if (it < tokenEnd)
						output.push_back(std::string(it, tokenEnd));
					it = tokenEnd;
				}
			}
		};

		inline void trim(std::string& toTrim, const std::string& delimeters = " \t\r\n") {
			//too bad it's pretty complicated with the IsSpace thing :(
			toTrim.erase(0, toTrim.find_first_not_of(delimeters));
			toTrim.erase(toTrim.find_last_not_of(delimeters) + 1);
		}

		template <typename T>
		std::string toString(const T& t) {
			std::ostringstream s;
			s << t;
			return s.str();
		}

		template <typename T>
		T toType(const std::string& s) {
			T result;
			std::istringstream is;
			is.str(s);
			is >> result;
			return result;
		}

		template <>
		inline bool toType<bool>(const std::string& s) {
			if (s == "true")
				return true;
			return false;
		}
	}
}